import sys
import os
import win32gui
import win32con
import win32api
import win32process
from typing import List, Dict, Set, Optional

from PySide6.QtWidgets import QApplication, QSystemTrayIcon, QMenu, QMessageBox
from PySide6.QtGui import QAction, QIcon
from PySide6.QtCore import QTimer, Qt, QObject, Signal

# -------------------- 全局配置 --------------------
CFG = {
    'hotkey': 'alt+d',
    'border_width': 3,
    'border_color': '#ff0000',
    'border_alpha': 200,
    'update_interval': 200,
    'hotkey_check_interval': 60000,  # 热键检查间隔（毫秒）
}

# -------------------- 全局变量 --------------------
_borders_dict: Dict[int, List[int]] = {}
_pinned: Set[int] = set()
_hotkey_window: int = 0
_hotkey_id: int = 1001
_registered_classes: Set[str] = set()
_last_hotkey_trigger_time: float = 0  # 最后触发时间


# -------------------- 核心功能 --------------------
def is_valid_window(hwnd: int) -> bool:
    """检查窗口是否有效"""
    if hwnd == 0 or not win32gui.IsWindow(hwnd) or not win32gui.IsWindowVisible(hwnd):
        return False

    _, pid = win32process.GetWindowThreadProcessId(hwnd)
    if pid == os.getpid():
        return False

    rect = win32gui.GetWindowRect(hwnd)
    return (rect[2] - rect[0]) > 10 and (rect[3] - rect[1]) > 10


def get_color_rgb(color_hex: str):
    """颜色转换"""
    color_hex = color_hex.lstrip('#')
    if len(color_hex) == 3:
        color_hex = ''.join([c * 2 for c in color_hex])
    return (int(color_hex[0:2], 16), int(color_hex[2:4], 16), int(color_hex[4:6], 16))


def draw_border(hwnd: int) -> List[int]:
    """绘制边框（不显示在任务栏）"""
    if not is_valid_window(hwnd) or CFG['border_width'] <= 0:
        return []

    try:
        rect = win32gui.GetWindowRect(hwnd)
        x, y, r, b = rect
        w, h = r - x, b - y
        width = CFG['border_width']
        color = get_color_rgb(CFG['border_color'])
        alpha = CFG['border_alpha']

        def create_border(x_pos, y_pos, w_size, h_size):
            class_name = "PinBorder_Class"
            if class_name not in _registered_classes:
                wc = win32gui.WNDCLASS()
                wc.lpszClassName = class_name
                wc.hInstance = win32api.GetModuleHandle(None)
                wc.lpfnWndProc = win32gui.DefWindowProc
                wc.hbrBackground = win32gui.CreateSolidBrush(win32api.RGB(*color))
                try:
                    win32gui.RegisterClass(wc)
                    _registered_classes.add(class_name)
                except:
                    pass

            # 关键：WS_EX_TOOLWINDOW 防止任务栏显示
            bor = win32gui.CreateWindowEx(
                win32con.WS_EX_TOPMOST | win32con.WS_EX_LAYERED |
                win32con.WS_EX_TRANSPARENT | win32con.WS_EX_TOOLWINDOW,
                class_name, None, win32con.WS_POPUP | win32con.WS_VISIBLE,
                x_pos, y_pos, w_size, h_size,
                None, None, win32api.GetModuleHandle(None), None
            )

            if bor:
                win32gui.SetLayeredWindowAttributes(bor, 0, alpha, win32con.LWA_ALPHA)
                win32gui.SetWindowPos(bor, win32con.HWND_TOPMOST, 0, 0, 0, 0,
                                      win32con.SWP_NOMOVE | win32con.SWP_NOSIZE)
            return bor

        borders = []
        borders.append(create_border(x - width, y - width, w + 2 * width, width))
        borders.append(create_border(x - width, b, w + 2 * width, width))
        borders.append(create_border(x - width, y, width, h))
        borders.append(create_border(r, y, width, h))

        return [b for b in borders if b]
    except Exception as e:
        print(f"绘制边框失败: {e}")
        return []


def clear_border(hwnd: int):
    """清除边框"""
    if hwnd in _borders_dict:
        for bor in _borders_dict[hwnd]:
            try:
                if win32gui.IsWindow(bor):
                    win32gui.DestroyWindow(bor)
            except Exception as e:
                print(f"销毁边框失败: {e}")
        del _borders_dict[hwnd]


def parse_hotkey(hotkey_str: str):
    """解析热键字符串"""
    modifiers = 0
    key = None
    parts = [p.strip() for p in hotkey_str.lower().split('+')]

    for part in parts:
        if part == 'ctrl':
            modifiers |= win32con.MOD_CONTROL
        elif part == 'alt':
            modifiers |= win32con.MOD_ALT
        elif part == 'shift':
            modifiers |= win32con.MOD_SHIFT
        elif part == 'win':
            modifiers |= win32con.MOD_WIN
        elif len(part) == 1 and part.isalnum():
            key = ord(part.upper())
        elif part in [f'f{i}' for i in range(1, 13)]:
            key = getattr(win32con, f'VK_{part.upper()}')

    return modifiers, key if key else 0


def toggle_pin():
    """切换窗口置顶"""
    hwnd = win32gui.GetForegroundWindow()
    if not is_valid_window(hwnd):
        return

    if hwnd in _pinned:
        win32gui.SetWindowPos(hwnd, win32con.HWND_NOTOPMOST, 0, 0, 0, 0,
                              win32con.SWP_NOMOVE | win32con.SWP_NOSIZE)
        clear_border(hwnd)
        _pinned.discard(hwnd)
        print(f"已取消置顶: {hwnd}")
    else:
        win32gui.SetWindowPos(hwnd, win32con.HWND_TOPMOST, 0, 0, 0, 0,
                              win32con.SWP_NOMOVE | win32con.SWP_NOSIZE)
        clear_border(hwnd)
        borders = draw_border(hwnd)
        if borders:
            _borders_dict[hwnd] = borders
            _pinned.add(hwnd)
            print(f"已置顶窗口: {hwnd}")


def update_borders():
    """更新边框位置（优化：减少CPU占用）"""
    for hwnd in list(_pinned):
        try:
            if not win32gui.IsWindow(hwnd) or win32gui.IsIconic(hwnd):
                clear_border(hwnd)
                _pinned.discard(hwnd)
                continue

            rect = win32gui.GetWindowRect(hwnd)
            x, y, r, b = rect
            w, h = r - x, b - y
            width = CFG['border_width']

            borders = _borders_dict.get(hwnd, [])
            if len(borders) == 4:
                # 使用SetWindowPos替代MoveWindow，减少重绘
                win32gui.SetWindowPos(borders[0], win32con.HWND_TOPMOST,
                                      x - width, y - width, w + 2 * width, width,
                                      win32con.SWP_NOACTIVATE)
                win32gui.SetWindowPos(borders[1], win32con.HWND_TOPMOST,
                                      x - width, b, w + 2 * width, width,
                                      win32con.SWP_NOACTIVATE)
                win32gui.SetWindowPos(borders[2], win32con.HWND_TOPMOST,
                                      x - width, y, width, h,
                                      win32con.SWP_NOACTIVATE)
                win32gui.SetWindowPos(borders[3], win32con.HWND_TOPMOST,
                                      r, y, width, h,
                                      win32con.SWP_NOACTIVATE)
        except Exception as e:
            print(f"更新边框失败: {e}")


# -------------------- 热键管理 --------------------
class HotkeyManager:
    def __init__(self):
        self.hotkey_registered = False
        self.last_check_time = 0

    def register(self) -> bool:
        """注册热键（带冲突检测和自动重试）"""
        global _hotkey_window

        self.unregister()

        if _hotkey_window == 0:
            self._create_hotkey_window()

        mod, key = parse_hotkey(CFG['hotkey'])
        if mod == 0 or key == 0:
            print(f"无效的热键配置: {CFG['hotkey']}")
            return False

        # 尝试注册，最多重试3次
        for attempt in range(3):
            try:
                win32gui.RegisterHotKey(_hotkey_window, _hotkey_id, mod, key)
                self.hotkey_registered = True
                print(f"热键注册成功: {CFG['hotkey']} (尝试 {attempt + 1})")
                return True
            except win32gui.error as e:
                if e.winerror == 1409:  # 热键被占用
                    print(f"热键被占用，{attempt + 1}秒后重试...")
                    # 短暂等待后重试
                    if attempt < 2:
                        import time
                        time.sleep(1)
                else:
                    print(f"热键注册失败: {e}")
                    break

        QMessageBox.warning(None, "警告",
                            f"热键 {CFG['hotkey']} 注册失败！\n可能被其他程序占用。")
        return False

    def unregister(self):
        """注销热键"""
        global _hotkey_window
        if self.hotkey_registered and _hotkey_window:
            try:
                win32gui.UnregisterHotKey(_hotkey_window, _hotkey_id)
                self.hotkey_registered = False
                print("热键已注销")
            except:
                pass

    def _create_hotkey_window(self):
        """创建热键监听窗口"""
        global _hotkey_window

        def wnd_proc(hwnd, msg, wParam, lParam):
            if msg == win32con.WM_HOTKEY and wParam == _hotkey_id:
                toggle_pin()
            elif msg == win32con.WM_POWERBROADCAST:
                # 系统电源事件（休眠/唤醒）
                if wParam == win32con.PBT_APMRESUMEAUTOMATIC:
                    print("系统从休眠恢复，重新注册热键...")
                    QTimer.singleShot(1000, self.register)  # 延迟1秒后重新注册
            return win32gui.DefWindowProc(hwnd, msg, wParam, lParam)

        wc = win32gui.WNDCLASS()
        wc.lpfnWndProc = wnd_proc
        wc.lpszClassName = "PinBorder_Hotkey"
        wc.hInstance = win32api.GetModuleHandle(None)

        try:
            win32gui.RegisterClass(wc)
        except:
            pass

        # 使用WS_EX_TOOLWINDOW样式，防止任务栏显示
        _hotkey_window = win32gui.CreateWindowEx(
            win32con.WS_EX_TOOLWINDOW,  # <- 新增
            wc.lpszClassName, "PinBorder",
            win32con.WS_CAPTION | win32con.WS_SYSMENU,
            0, 0, 100, 100, None, None, wc.hInstance, None
        )
        win32gui.ShowWindow(_hotkey_window, win32con.SW_HIDE)

    def verify_hotkey(self) -> bool:
        """验证热键是否仍然有效（通过注册测试）"""
        mod, key = parse_hotkey(CFG['hotkey'])
        try:
            # 尝试用不同的ID临时注册，如果成功说明系统热键功能正常
            win32gui.RegisterHotKey(_hotkey_window, 9999, mod, key)
            win32gui.UnregisterHotKey(_hotkey_window, 9999)
            return True
        except:
            return False


# -------------------- 主程序 --------------------
class PinBorderApp(QSystemTrayIcon):
    def __init__(self):
        super().__init__()
        self.hotkey_manager = HotkeyManager()
        self.init_ui()
        self.setup_hotkey()

        # 定时器：更新边框
        self.border_timer = QTimer()
        self.border_timer.timeout.connect(update_borders)
        self.border_timer.start(CFG['update_interval'])

        # 定时器：热键健康检查
        self.hotkey_check_timer = QTimer()
        self.hotkey_check_timer.timeout.connect(self.check_hotkey_health)
        self.hotkey_check_timer.start(CFG['hotkey_check_interval'])

    def init_ui(self):
        """初始化系统托盘"""
        # 使用本地图标文件而不是系统主题图标
        import os
        icon_path = os.path.join(os.path.dirname(__file__), 'img', 'logo.png')
        if os.path.exists(icon_path):
            self.setIcon(QIcon(icon_path))
        else:
            # 如果找不到图标文件，则回退到系统主题图标
            pixmap = QIcon.fromTheme("window-new").pixmap(64, 64)
            self.setIcon(QIcon(pixmap))
        self.setToolTip(f"PinBorder\n热键: {CFG['hotkey']}")

        menu = QMenu()
        quit_action = QAction("退出", menu)
        quit_action.triggered.connect(self.quit)
        menu.addAction(quit_action)
        self.setContextMenu(menu)

    def setup_hotkey(self):
        """初始化热键"""
        if not self.hotkey_manager.register():
            # 10秒后重试
            QTimer.singleShot(10000, self.setup_hotkey)

    def check_hotkey_health(self):
        """检查热键健康状态"""
        if not self.hotkey_manager.hotkey_registered:
            print("热键未注册，尝试重新注册...")
            self.hotkey_manager.register()
            return

        # 验证系统热键功能是否正常
        if not self.hotkey_manager.verify_hotkey():
            print("热键功能异常，重新注册...")
            self.hotkey_manager.register()

    def quit(self):
        """退出程序"""
        self.hotkey_manager.unregister()
        for hwnd in list(_pinned):
            clear_border(hwnd)
        QApplication.quit()


def main():
    app = QApplication(sys.argv)
    app.setQuitOnLastWindowClosed(False)

    tray = PinBorderApp()
    tray.show()

    print(f"PinBorder 启动成功！热键: {CFG['hotkey']}")
    print("右键托盘图标可退出")

    sys.exit(app.exec())


if __name__ == '__main__':
    main()